#include <STC15F2K60S2.H>
#include <led.h>
#include <seg.h>
#include <key.h>
#include <ds1302.h>
#include <intrins.h>
/**
0.蜂鸣器原理图：见pdf文件

1. 如何控制数码管500毫米闪烁，以控制ucRtc[0]小时为例？
（1）增加全局变量：unsigned char uwTick,seg_flag;控制全局心跳和数码管闪烁标志；
（2）增加定时器1中断服务函数：Timer1_Server()，在中断服务函数中，ucTick++；
（3）Timer1_Server()中增加代码，500毫秒取反一次：
        if(uwTick%500==0)seg_flag=!seg_flag;//seg_flag=seg_flag^1;
（4）seg_proc()中增加代码，根据seg_flag控制数码管显示：
        if(seg_flag==1){
            Seg_Buf[0]=ucRtc[0]/10;
            Seg_Buf[1]=ucRtc[0]%10;

        }else{
            Seg_Buf[0]=10;
            Seg_Buf[1]=10;
        }
        简化为：
        Seg_Buf[0]=seg_flag?ucRtc[0]/10:10;
        Seg_Buf[1]=seg_flag?ucRtc[0]%10:10;
经过以上步骤，就可以实现数码管前两位的500毫秒闪烁功能。
思考：如何实现数码管后两位的500毫秒闪烁功能？
2. 如何控制蜂鸣器？->beep();函数
3. 如何使用按键控制蜂鸣器？->见key_proc(),按键4和5
4. 闹钟功能：
整体要求：
    按键6：正常显示模式；
    按键7：设置闹钟模式；
    按键8：选择小时、分钟、秒；选中后500毫秒间隔闪烁；
    按键9：加1，按键10：减1；
    按键11：确定；
    按键12：取消；
    当按键11：确定时，设置的时间生效，当按键12：取消时，设置的时间无效。
    当时间到达设置的时间时，蜂鸣器响铃。
实现步骤：
（1）增加全局变量存储闹钟时间：unsigned char ucClock[]={23,59,30};//{时，分，秒}
增加全局变量存储闹钟设置时的时间：unsigned char ucSetClock[3];
增加全局变量存储数码管显示模式：unsigned char seg_mode;//表示显示模式：0-正常，1-设置  Key6 Key7
增加全局变量存储当前选择的组：unsigned char seg_index;//表示当前选择的组：0-小时，1-分钟，2-秒，Key8
（2）增加按键处理函数：key_proc()，在按键处理函数中，实现按键6和7的显示模式和设置模式切换功能：
    switch (Key_Down)
    {
    //存在的代码
        case 6:
            seg_mode=0;//正常显示模式
            break;
        case 7:
            seg_mode=1;//设置闹铃模式
            ucSetClock[0]=ucClock[0];//保存设置时的时
            ucSetClock[1]=ucClock[1];//保存设置时的分
            ucSetClock[2]=ucClock[2];//保存设置时的秒
            break;
        case 8: // 确定选择那一组：时分秒
            seg_index = (++seg_index) % 3;//seg_index在0-2之间循环
            break;
    }
（3）增加按键处理函数：key_proc()，在按键处理函数中，实现按键9和10的功能，实现加1和减1操作：
    switch (Key_Down)
    {
        //存在的代码
        case 9: // 加
            if (seg_index == 0)
                ucSetClock[0] = (++ucSetClock[0]) % 24;//小时在0-23之间循环
            else
                ucSetClock[seg_index] = (++ucSetClock[seg_index]) % 60;//分钟和秒在0-59之间循环
            break;
        case 10: // 减，此处有问题，思考为什么？
            if (seg_index == 0)
                ucSetClock[0] = (--ucSetClock[0]) % 24;//小时在0-23之间循环
            else
                ucSetClock[seg_index] = (--ucSetClock[seg_index]) % 60;//分钟和秒在0-59之间循环
    }
（4）增加按键处理函数：key_proc()，在按键处理函数中，实现按键11和12的功能，实现确定和取消操作：
    switch (Key_Down)
    {
        //存在的代码
        case 11: // 确定
            ucClock[0]=ucSetClock[0];//保存设置时的时
            ucClock[1]=ucSetClock[1];//保存设置时的分
            ucClock[2]=ucSetClock[2];//保存设置时的秒
            break;
        case 12: // 取消
            ucSetClock[0]=ucClock[0];//保存设置时的时
            ucSetClock[1]=ucClock[1];//保存设置时的分
            ucSetClock[2]=ucClock[2];//保存设置时的秒
            break;
    }
（5）增加数码管显示函数：seg_proc()，在数码管显示函数中，实现数码管的显示：
    void seg_proc() {
        if (seg_mode == 0) { // 正常显示模式
            Seg_Buf[0] = ucRtc[0] / 10;
            Seg_Buf[1] = ucRtc[0] % 10;
            Seg_Buf[2] = 10;
            Seg_Buf[3] = ucRtc[1] / 10;
            Seg_Buf[4] = ucRtc[1] % 10;
            Seg_Buf[5] = 10;
            Seg_Buf[6] = ucRtc[2] / 10;
            Seg_Buf[7] = ucRtc[2] % 10;
        } else { // 设置闹铃模式，同时通过seg_index控制当前选择的组实现500毫秒闪烁功能
            if (seg_index == 0) { // 小时
                Seg_Buf[0] = seg_flag ? ucSetClock[0] / 10 : 10;
                Seg_Buf[1] = seg_flag ? ucSetClock[0] % 10 : 10;
                Seg_Buf[2] = 10;
                Seg_Buf[3] = ucSetClock[1] / 10;
                Seg_Buf[4] = ucSetClock[1] % 10;
                Seg_Buf[5] = 10;
                Seg_Buf[6] = ucSetClock[2] / 10;
                Seg_Buf[7] = ucSetClock[2] % 10;
            } else if (seg_index == 1) { // 分钟
                Seg_Buf[0] = ucSetClock[0] / 10;
                Seg_Buf[1] = ucSetClock[0] % 10;
                Seg_Buf[2] = 10;
                Seg_Buf[3] = seg_flag? ucSetClock[1] / 10 : 10; // 分钟
                Seg_Buf[4] = seg_flag? ucSetClock[1] % 10 : 10; // 分钟
                Seg_Buf[5] = 10;
                Seg_Buf[6] = ucSetClock[2] / 10;
                Seg_Buf[7] = ucSetClock[2] % 10;
            } else { // 秒
                Seg_Buf[0] = ucSetClock[0] / 10;
                Seg_Buf[1] = ucSetClock[0] % 10;
                Seg_Buf[2] = 10;
                Seg_Buf[3] = ucSetClock[1] / 10;
                Seg_Buf[4] = ucSetClock[1] % 10;
                Seg_Buf[5] = 10;
                Seg_Buf[6] = seg_flag? ucSetClock[2] / 10 : 10; // 秒
                Seg_Buf[7] = seg_flag? ucSetClock[2] % 10 : 10; // 秒
            }
            //简化代码：*************
            Seg_Buf[0] = seg_index == 0? seg_flag? ucSetClock[0] / 10 : 10 : ucSetClock[0] / 10;
            Seg_Buf[1] = seg_index == 0? seg_flag? ucSetClock[0] % 10 : 10 : ucSetClock[0] % 10;
            Seg_Buf[2] = 10;
            Seg_Buf[3] = seg_index == 1? seg_flag? ucSetClock[1] / 10 : 10 : ucSetClock[1] / 10;
            Seg_Buf[4] = seg_index == 1? seg_flag? ucSetClock[1] % 10 : 10 : ucSetClock[1] % 10;
            Seg_Buf[5] = 10;
            Seg_Buf[6] = seg_index == 2? seg_flag? ucSetClock[2] / 10 : 10 : ucSetClock[2] / 10;
            Seg_Buf[7] = seg_index == 2? seg_flag? ucSetClock[2] % 10 : 10 : ucSetClock[2] % 10;

            // 进一步使用for循环简化代码：*************
            unsigned char i;
            Seg_Buf[2] =Seg_Buf[5]= 10;
            for (i = 0; i < 3; i++) {
                Seg_Buf[i * 3] = seg_index == i? seg_flag? ucSetClock[i] / 10 : 10 : ucSetClock[i] / 10;
                Seg_Buf[i * 3 + 1] = seg_index == i? seg_flag? ucSetClock[i] % 10 : 10 : ucSetClock[i] % 10;
            }
        }
（6）修改定时器1中断服务函数：Timer1_Server()，在正常显示模式下，当闹铃设置时间达到时实现响铃功能：
void Timer1_Server() interrupt 3 {
    //存在的代码
    if(seg_mode==0){
        if(ucRtc[0]==ucClock[0] && ucRtc[1]==ucClock[1] && ucRtc[2]==ucClock[2] )
        {
            beep(1);
        }
    }
}
5.作业：增强闹铃功能：
（1）当时间到达设置的时间时，led灯闪烁；
（2）当时间到达设置的时间时，led灯闪烁10次，然后停止；
（3）当时间到达设置的时间时，led灯闪烁10次，然后停止，然后继续闪烁；
（4）设置3个闹钟，分别是：
1. 23:59:55
2. 23:59:56
3. 23:59:57
当时间到达设置的时间时，led灯闪烁10次，然后停止，然后继续闪烁；
*/

unsigned char ucLed[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long int uwTick; // 全局心跳
unsigned char Key_Val, Key_Down, Key_Up, Key_Old;
unsigned char ucRtc[] = {23, 59, 55};                  //{时，分，秒}
unsigned char ucClock[] = {23, 59, 30}, ucSetClock[3]; //{时，分，秒}

unsigned char Seg_Pos;
unsigned char Seg_Buf[] = {10, 10, 10, 10, 10, 10, 10, 10};
unsigned char is_led_flag;  // 标记第1个led灯的闪烁状态，每200ms翻转一次
unsigned char is_led_start; // led灯的闪烁计数器，闪烁10次

unsigned char seg_flag, seg_index = 0; // seg_index 第几组
unsigned char seg_mode;                // 表示显示模式：0-正常，1-设置  Key6 Key7

// 1. 响铃

void key_proc()
{
    Key_Val = keyRead();
    Key_Down = Key_Val & (Key_Val ^ Key_Old);
    Key_Up = Key_Val & (Key_Val ^ Key_Old);
    Key_Old = Key_Val;
    switch (Key_Down)
    {
    case 4:
        beep(1); // 开启蜂鸣器
        break;
    case 5:
        beep(0); // 关闭蜂鸣器
        break;
    case 6:
        seg_mode = 0; // 正常显示模式
        break;
    case 7:
        seg_mode = 1;               // 设置闹铃模式
        ucSetClock[0] = ucClock[0]; // 保存设置时的时
        ucSetClock[1] = ucClock[1]; // 保存设置时的分
        ucSetClock[2] = ucClock[2]; // 保存设置时的秒
        break;
    case 8: // 确定选择那一组：时分秒
        seg_index = (++seg_index) % 3;
        break;
    case 9: // 加
        if (seg_index == 0)
            ucSetClock[0] = (++ucSetClock[0]) % 24;
        else
            ucSetClock[seg_index] = (++ucSetClock[seg_index]) % 60;
        break;
    case 10: // 减,注意：不要减到0，思考为什么？以下代码有什么问题？
        if (seg_index == 0)
            ucSetClock[0] = (--ucSetClock[0]) % 24;
        else
            ucSetClock[seg_index] = (--ucSetClock[seg_index]) % 60;
        /**
         * 问题：
         * 1. 当减到0时，会一直减，直到减到255，然后再减到0，然后再减到255，
         * 这样就会一直减，直到减到0，然后再减到255，这样就会一直减
         * 解决：
         * 1. 当减到0时，不进行减操作，直接返回
         * 2. 当减到0时，将值设置为23或者59，这样就不会一直减到0，然后再减到255，
         * 这样就会一直减
         * 修改后的代码如下：
         * if (seg_index == 0) {
         *     if (ucSetClock[0] == 0)
         *         ucSetClock[0] = 23;
         *     else
         *         ucSetClock[0] = (--ucSetClock[0]) % 24;
         * } else {
         *     if (ucSetClock[seg_index] == 0)
         *         ucSetClock[seg_index] = 59;
         *     else
         *         ucSetClock[seg_index] = (--ucSetClock[seg_index]) % 60;
         * }
         *
         *
         */
        break;
    }
}
void led_proc()
{
}
void seg_proc()
{ // 根据ucRtc显示到数码管中，中间使用“-”分隔
    // ucRtc[0]是小时，ucRtc[1]是分钟，ucRtc[2]是秒
    //	if(seg_flag==1){
    //		Seg_Buf[0]=ucRtc[0]/10;
    //		Seg_Buf[1]=ucRtc[0]%10;//10
    //	}
    //	else {
    //		Seg_Buf[0]=10;
    //		Seg_Buf[1]=10;
    //	}
    if (seg_mode == 1)
    { // 设置状态，灯闪：seg_index为选择的时分秒的索引
        // seg_index
        switch (seg_index)
        {
        case 0:
            Seg_Buf[0] = (seg_falg == 1 ? ucClock[0] / 10 : 10);
            Seg_Buf[1] = (seg_falg == 1 ? ucClock[0] % 10 : 10);
            Seg_Buf[3] = ucClock[1] / 10;
            Seg_Buf[4] = ucClock[1] % 10;
            Seg_Buf[6] = ucClock[2] / 10;
            Seg_Buf[7] = ucClock[2] % 10;
            break;
        case 1:
            Seg_Buf[0] = ucClock[1] / 10;
            Seg_Buf[1] = ucClock[1] % 10;
            Seg_Buf[3] = (seg_falg == 1 ? ucClock[1] / 10 : 10);
            Seg_Buf[4] = (seg_falg == 1 ? ucClock[1] % 10 : 10);
            Seg_Buf[6] = ucClock[2] / 10;
            Seg_Buf[7] = ucClock[2] % 10;
            break;
        case 2:
            Seg_Buf[0] = ucClock[1] / 10;
            Seg_Buf[1] = ucClock[1] % 10;

            Seg_Buf[3] = ucClock[2] / 10;
            Seg_Buf[4] = ucClock[2] % 10;
            Seg_Buf[6] = (seg_falg == 1 ? ucClock[2] / 10 : 10);
            Seg_Buf[7] = (seg_falg == 1 ? ucClock[2] % 10 : 10);
            break;
        }
        // 简化代码：
        /*
        unsigned char i;
        Seg_Buf[2] =Seg_Buf[5]= 10;
        for (i = 0; i < 3; i++) {
            Seg_Buf[i * 3] = seg_index == i? seg_flag? ucSetClock[i] / 10 : 10 : ucSetClock[i] / 10;
            Seg_Buf[i * 3 + 1] = seg_index == i? seg_flag? ucSetClock[i] % 10 : 10 : ucSetClock[i] % 10;
        }
        */
    }
    else
    {
        Seg_Buf[0] = ucRtc[0] / 10;
        Seg_Buf[1] = ucRtc[0] % 10;
        Seg_Buf[2] = 10;
        Seg_Buf[3] = ucRtc[1] / 10;
        Seg_Buf[4] = ucRtc[1] % 10;
        Seg_Buf[5] = 10;
        Seg_Buf[6] = ucRtc[2] / 10;
        Seg_Buf[7] = ucRtc[2] % 10;
        // 简化代码：
        /*
        unsigned char i;
        Seg_Buf[2] =Seg_Buf[5]= 10;
        for (i = 0; i < 3; i++) {
            Seg_Buf[i * 3] = ucRtc[i] / 10;
            Seg_Buf[i * 3 + 1] = ucRtc[i] % 10;
        }
        */
    }
}
void Timer1_Server() interrupt 3
{
    uwTick++; // 系统计时增加

    // 1s从DS1302中取一次时钟的值存放于ucRtc中
    if (uwTick % 1000 == 0)
    {
        Read_Rtc(ucRtc);
    }
    if (uwTick % 200 == 0)
    {
        is_led_flag = is_led_flag ^ 1;
    }

    if (uwTick % 500 == 0)
        seg_flag = !seg_flag; // seg_flag=seg_flag^1;

    if (seg_mode == 0)
    {
        if (ucRtc[0] == ucClock[0] && ucRtc[1] == ucClock[1] && ucRtc[2] == ucClock[2])
        {
            beep(1);
        }
    }

    led_display(ucLed);

    Seg_Pos = (++Seg_Pos) % 8; // 更新数码管位置,比原模版更省时间
    // 数码管显示处理
    //  if (Seg_Buf[Seg_Pos] > 20)
    //		seg_display(Seg_Pos, Seg_Buf[Seg_Pos] - ',', 1); // 带小数点
    //  else
    seg_display(Seg_Pos, Seg_Buf[Seg_Pos], 0); // 无小数点
}

void Timer1_Init(void)
{
    AUXR &= 0xBF; // 定时器时钟12T模式
    TMOD &= 0x0F; // 设置定时器模式
    TL1 = 0x18;   // 设置定时初始值
    TH1 = 0xFC;   // 设置定时初始值
    TF1 = 0;      // 清除TF1标志
    TR1 = 1;      // 启动定时器1
    ET1 = 1;      // 使能定时器1中断
    EA = 1;       // 使能总中断
}
void main()
{
    Timer1_Init();
	beep(0);
    // 初始化时钟
    Set_Rtc(ucRtc);

    while (1)
    {
        key_proc();
        led_proc();
        seg_proc();
    }
}